package com.wd.study.pattern.singleton;

/**
 * 单例模式<BR>
 *
 * @author w4425
 * @version [V2.0.0, 2021/3/19]
 * @since V2.0.0
 */
public class xxx {
    private static int a = 1;

    /**
     * static 静态变量
     * 生命周期：起始于类的加载，终止于类的释放。
     * app启动。创建进程。初始化dvm实例，负则类加载释放【进程创建后，就会加载类，静态变量就实例化了】进程结束时，静态变量就销毁了
     * 1.不能保证静态变量会一直存在.(进程可能被Kill掉)
     * 2.每次打开app时静态变量的值都是初始值(进程没有被kill掉所以静态变量保存的还是上次的值)。
     * 而且，静态变量是不会被垃圾回收的，其对象一直保持引用，及ARC不可能是0。
     * 所以要自己释放静态变量。
     * 3.Application其实是一个单例对象，也是放在内存中的，当进程被杀掉，就全清空了，只不过Android系统会帮重建Application，而我们存放在Application的数据自然就没有了，还是得自己处理。
     * 4.静态引用的对象不会被垃圾回收
     * 只要静态变量没有被销毁也没有置null，其对象一直被保持引用，也即引用计数不可能是0，因此不会被垃圾回收。因此，单例对象在运行时不会被回收。
     */
    /**
     * 饿汉式
     * 里面的对象是个静态对象，第一次声明的时候就会实现初始化。
     */
    public static class Singletion1 {
        private static final Singletion1 mSingletion1 = new Singletion1();

        private Singletion1() {
        }

        public static Singletion1 getInstance() {
            return mSingletion1;
        }
    }

    /**
     * 懒汉式
     * 在使用时才会被实例化，一定程度上节约了资源。
     * [缺点]单例在第一次加载时要及时进行实例化，反应稍慢
     */
    public static class Singleton2 {
        private static Singleton2 mSingleton;

        private Singleton2() {
        }

        public static synchronized Singleton2 getInstance() {
            if (mSingleton == null) {
                mSingleton = new Singleton2();
            }
            return mSingleton;
        }
    }

    /**
     * 1双重检查锁
     * 这个getInstance方法中对mSingleton进行了两次判空：第一次是为了避免不必要的同步锁；第二层是为了在null的时候创建实例。
     * 2DCL失效：
     * 在多线程下，假设A线程执行到mSingleton=new Singleton()的时候，CPU并不是一次性执行完这条语句的，因为这不是一个原子操作（指不会被线程调度机制打断的操作）。
     * 3mSingleton=new Singleton()大致做了三件事：
     * 给Singleton的实例分配内存
     * 调用Singleton的构造方法
     * 将mSingleton指向分配的内存空间（这个时候mSingleton才不为空）
     * 由于Java编译器允许处理器乱序执行，所以上面的第二步第三步的执行顺序没法得到保证。执行顺序可能是1-2-3也可能是1-3-2。
     * 当A线程执行顺序是1-3-2的时候，如果执行到了1-3，第2步还没执行的时候，如果B线程判断mSingleton==null的时候就会的发哦FALSE的结果，
     * 从而返回一个错误的单例。
     * 【优点】资源利用率高，第一次执行getInstance的时候才会被实例化，效率高。
     * 【缺点】第一册加载反应稍慢，而且有失败的可能，但是概率很小。
     */
    public static class Singleton3 {
        private static volatile Singleton3 mSingleton;

        private Singleton3() {
        }

        public static Singleton3 getInstance() {
            if (mSingleton == null) {
                synchronized (Singleton3.class) {
                    if (mSingleton == null) {
                        mSingleton = new Singleton3();
                    }
                }
            }
            return mSingleton;
        }
    }

    /**
     * 静态内部类
     * 当第一次加载Singleton的时候并不会初始化mSingleton，只有在第一次调用getInstance的时候才会加载SIngletonHolder类。
     * [优点]
     * 不仅能保证线程安全，也能保证单例的唯一性，也延迟了单例的实例化，比较推荐
     * 为什么静态内部类是线程安全的？
     * JVM 在类初始化期间会获取这个初始化锁，并且每个线程至少获取一次锁来确保这个类已经被初始化过了。（但是只有第一个获得锁的线程，会执行初始化，
     * 执行完之后会设置一个标志位，表示已经初始化完成，后面其他的线程再次获得锁，检查标志位，发现已经初始化完了，直接释放锁，不会再次执行初始化。
     */
    public static class Singleton4 {
        private Singleton4() {
        }

        public static Singleton4 getInstance() {
            return SingletonHolder.mSingleton;
        }

        private static class SingletonHolder {
            private static final Singleton4 mSingleton = new Singleton4();
        }
    }

    /**
     * 枚举单例
     * 使用时可以通过Singleton singleton = Singleton.INSTANCE;来获取单例。
     * [优点]写法简单，而且默认线程安全，任何情况下都是一个单例。
     */
    public enum Singleton5 {
        INSTANCE;

        public void doThing() {
            System.out.println(this.hashCode());
        }
    }
}
